<template>
  <div class="chat-container">

    <!-- 对话区域 -->
    <el-card class="message-box">
      <div class="message-list">
        <!-- 遍历消息列表 -->
        <div v-for="(msg, index) in messages" :key="index" class="message-item" :class="{ 'right': msg.isUser }">
          <!-- 头像部分 -->
          <div class="avatar" :class="{ 'user-avatar': msg.isUser, 'ai-avatar': !msg.isUser }">
            <img :src="msg.isUser ? userAvatar : aiAvatar" alt="Avatar" class="avatar-img"/>
          </div>

          <!-- 消息气泡 -->
          <div class="message-bubble" :class="{ 'user': msg.isUser, 'ai': !msg.isUser }">
            <vue-markdown :source="msg.content"/>
          </div>
        </div>
      </div>
    </el-card>

    <!-- 输入区域 -->
    <div class="input-area">
      <el-input
          v-model="inputMessage"
          @keyup.enter.native="sendMessage"
          placeholder="请输入消息"
          :disabled="isSending"
          class="input-field"
      />
      <el-button type="primary" @click="handleButtonClick" class="chat-btn">
        {{ isSending ? '终止' : '发送' }}
      </el-button>
    </div>

  </div>
</template>

<script>
import VueMarkdown from 'vue-markdown'

export default {
  components: {
    VueMarkdown
  },
  data() {
    return {
      messages: [], // 消息列表
      inputMessage: '', // 用户输入的消息
      eventSource: null, // SSE 事件源
      isSending: false, // 发送状态
      userAvatar: require('../images/user.png'), // 用户头像路径
      aiAvatar: require('../images/ai.png') // AI 头像路径
    };
  },
  watch: {
    messages() {
      this.scrollToBottom();
    }
  },
  methods: {
    sendMessage() {
      if (this.isSending) {
        return;
      }
      this.isSending = true;
      if (this.inputMessage.trim()) {
        // 添加用户消息到消息列表
        this.messages.push({
          content: this.inputMessage,
          isUser: true
        });

        this.messages.push({
          content: '',
          isUser: false
        });
        const lastIndex = this.messages.length - 1;
        this.appendMessageContent('正在思考......', lastIndex, 80);

        // 初始化 SSE 并发送消息
        this.connectSSE(this.inputMessage);

        // 清空输入框
        this.inputMessage = '';
      }
    },
    connectSSE(message) {
      // 创建 SSE 连接
      this.eventSource = new EventSource(`/api/streamingAssistant?message=${encodeURIComponent(message)}`);

      // 连接成功后，将发送状态设置为 true
      this.eventSource.onopen = () => {
        this.messages[this.messages.length - 1].content = '';
      };

      // 标志位：表示是否正在处理消息
      this.isProcessCount = 0;
      this.isProcessing = false;

      // 监听服务器推送的消息
      this.eventSource.onmessage = async (event) => {
        if (this.eventSource === null) {
          return;
        }
        // 处理计数加1
        this.isProcessCount++;

        // 如果当前正在处理消息，则等待
        while (this.isProcessing) {
          // 等待直到前一个处理完成
          await this.waitUntilAvailable();
        }
        // 设置标志位为 true，表示开始处理
        this.isProcessing = true;
        try {
          // 获取最新消息的索引
          const lastIndex = this.messages.length - 1;
          // 调用逐字更新方法
          await this.processEventMessage(event.data, lastIndex);
        } finally {
          // 处理完成后，释放标志位
          this.isProcessing = false;
          this.isProcessCount--;
          if (this.isProcessCount === 0) {
            this.isSending = false;
            this.eventSource = null;
          }
        }
      };

      this.eventSource.onclose = () => {
        this.eventSource = null;
      };

      // 处理连接关闭或错误
      this.eventSource.onerror = () => {
        this.eventSource.close();
      };
    },
    waitUntilAvailable() {
      return new Promise((resolve) => {
        const interval = setInterval(() => {
          if (!this.isProcessing) {
            // 停止定时器
            clearInterval(interval);
            resolve();
          }
        }, 50);
      });
    },
    processEventMessage(data, messageIndex) {
      return new Promise((resolve) => {
        //TODO
        const dataArray = data.split("");
        // 当前处理的字符索引
        let currentIndex = 0;
        // 定义一个递归函数，逐字更新指定消息的内容
        const updateChar = () => {
          if (currentIndex < dataArray.length && this.eventSource !== null) {
            this.setMessageContent(messageIndex, this.messages[messageIndex].content + dataArray[currentIndex]);
            if (currentIndex > 0 && currentIndex % 20 === 0) {
              this.setMessageContent(messageIndex, this.messages[messageIndex].content + "\n");
            }
            currentIndex++;
            setTimeout(updateChar, 20);
          } else {
            // 所有字符处理完成，结束 Promise
            resolve();
          }
        };
        // 开始逐字更新
        updateChar();
      });
    },
    appendMessageContent(data, messageIndex, delay) {
      const dataArray = data.split("");
      let currentIndex = 0;
      const updateChar = () => {
        if (currentIndex < dataArray.length) {
          this.setMessageContent(messageIndex, this.messages[messageIndex].content + dataArray[currentIndex]);
          if (currentIndex > 0 && currentIndex % 20 === 0) {
            this.setMessageContent(messageIndex, this.messages[messageIndex].content + "\n");
          }
          currentIndex++;
          setTimeout(updateChar, delay);
        }
      };
      // 开始逐字更新
      updateChar();
    },
    setMessageContent(messageIndex, content) {
      this.$set(
          this.messages[messageIndex],
          "content",
          content
      );
    },
    handleButtonClick() {
      if (this.isSending) {
        // 如果正在发送消息，则终止 SSE 连接
        if (this.eventSource) {
          this.eventSource.close();
          this.eventSource = null;
        }
      } else {
        // 否则发送消息
        this.sendMessage();
      }
    },
    scrollToBottom() {
      this.$nextTick(() => {
        const messageBox = this.$el.querySelector('.message-box');
        if (messageBox) {
          messageBox.scrollTop = messageBox.scrollHeight;
        }
      });
    }
  },
  beforeDestroy() {
    // 组件销毁时关闭 SSE 连接
    if (this.eventSource) {
      this.eventSource.close();
    }
  }
};
</script>

<style scoped>
/* 整体容器 */
.chat-container {
  margin-left: 20vh; /* 留出左侧边距 */
  margin-right: 20vh; /* 留出右侧边距 */
  display: flex; /* 使用 Flexbox 布局 */
  flex-direction: column; /* 垂直布局 */
  height: 80vh;
}

/* 对话区域 */
.message-box {
  flex: 1; /* 让对话区域占据剩余空间 */
  margin-bottom: 20px; /* 与输入区域保持间距 */
  overflow-y: auto; /* 允许滚动 */
  min-height: 400px; /* 最小高度 */
  max-height: 60vh; /* 最大高度 */
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.12); /* 阴影效果 */
  border-radius: 25px;
}

/* 消息列表 */
.message-list {
  display: flex;
  flex-direction: column;
  padding: 10px; /* 内边距 */
}

/* 消息项 */
.message-item {
  display: flex;
  align-items: flex-start; /* 垂直对齐顶部 */
  gap: 10px; /* 头像与气泡之间的间距 */
  margin-top: 15px;
}

/* 用户消息右对齐 */
.message-item.right {
  justify-content: flex-end; /* 右对齐 */
}

/* AI 消息左对齐 */
.message-item:not(.right) {
  justify-content: flex-start; /* 左对齐 */
}

/* 头像样式 */
.avatar {
  width: 47px;
  height: 47px;
  border-radius: 26%; /* 圆形头像 */
  overflow: hidden;
}

.avatar-img {
  width: 100%;
  height: 100%;
  object-fit: cover; /* 确保图片填充完整 */
}

/* 用户头像在右侧 */
.message-item.right .avatar {
  order: 2; /* 用户头像放在右侧 */
}

/* AI 头像在左侧 */
.message-item:not(.right) .avatar {
  order: 0; /* AI 头像放在左侧 */
}

/* 消息气泡样式 */
.message-bubble {
  max-width: 70%;
  padding: 6px 16px;
  border-radius: 10px;
  word-break: break-word;
  display: flex;
  flex-direction: column; /* 垂直排列子元素 */
}

/* Markdown内容样式 */
.message-bubble >>> pre {
  background: rgba(0, 0, 0, 0.1);
  padding: 10px;
  border-radius: 6px;
  overflow-x: auto;
}

.message-bubble >>> code {
  font-family: 'Consolas', monospace;
  font-size: 0.9em;
}

.message-bubble >>> p {
  margin: 0.5em 0;
}

/* 用户消息气泡样式 */
.message-bubble.user {
  background-color: #409eff; /* 浅灰色背景 */
  color: white; /* 黑色文字 */
  order: 1; /* 用户消息气泡在右侧 */
}

/* AI 消息气泡样式 */
.message-bubble.ai {
  background-color: #f5f7fa; /* 蓝色背景 */
  color: black; /* 白色文字 */
  order: 1; /* AI 消息气泡在左侧 */
}

/* 输入区域 */
.input-area {
  display: flex;
  gap: 10px; /* 输入框和按钮之间的间距 */
}

/* 输入框样式 */
.input-field {
  flex: 1; /* 输入框占据剩余空间 */
  border-radius: 10px; /* 圆角 */
}

/* 发送按钮 */
.chat-btn {
  border-radius: 10px; /* 圆角 */
}
</style>